import { 
    TAllTypes,
    IValueNumber,
    DAY_MUNIS,
    IExcelValue,
    IValueBoolean,
    IValueString,
    IValue,
    IPosition,
    IFunction
} from '../types';
import { Injectable, InjectFactory } from '../../../decorators/inject';
import { isEqualVal, parseToNumber, typeofValue } from '../utils'
import { FormulaError } from '../formula-error';
import { AFunc, IPositionValue } from './types';
import { DefaultFuncs } from './funcs'; 
class NotFoundFunc extends AFunc {
  constructor(public errorFuncName: string) {
    super({getValue: () => []});
  }
  public excute(args: TAllTypes[]): TAllTypes[] {
    throw new FormulaError('#UNSUPPORT', {});
  }
}
@Injectable('FunctionManange')
export class FunctionManange {
  protected funsMap: Map<string, {new(...args:any[]): AFunc}> = new Map();
  constructor(
    @InjectFactory() protected factory: any
  ) {

  }
  public init() {
    for(let item of DefaultFuncs) {
      this.register(item.name, item.value);
    }
  }
  public register(name: string, func: {new(...args:any[]): AFunc}) {
    if(!this.isExistFunc(name)) {
      this.funsMap.set(name, func);
    }
  }
  public remove(name: string) {
    this.funsMap.delete(name);
  }
  public isExistFunc(name: string) {
    return this.funsMap.has(name);
  }
  public getFunc(name: string) {
    if(this.isExistFunc(name)) {
      let func = this.funsMap.get(name);
      let funcInstance = this.factory(func) as AFunc;
      return funcInstance;
    } else {
      return new NotFoundFunc(name);
    }
  }
}
export type mapAnyTypeToRangeValue = TAllTypes&IExcelValue
// function groupByCol<T extends (Required<IExcelValue>&(IValueBoolean|IValueNumber|IValueString))>(vals: T[]) {
//     let temp: {[key: number]: T[]} = {};
//     for(let val of vals) {
//         if(temp[val.col] === undefined) {
//             temp[val.col] = [];
//         }
//         temp[val.col].push(val);
//     }
//     let result: {col: number, vals: T[]}[] = [];
//     for(let key in temp) {
//         result.push({
//             col: Number(key),
//             vals: temp[key]
//         })
//     }
//     return result.sort((a, b) => a.col - b.col);
// }
// export function formatValsToGroup(vals: mapAnyTypeToRangeValue[]) {
//     if(vals.length === 0) return [];
//     let result: mapAnyTypeToRangeValue[][] = [];
//     let currentGroup: mapAnyTypeToRangeValue[] = [];
//     let currentGroupSymbol = vals[0].rangeId
//     for(let val of vals) {
//         if(val.range !== currentGroupSymbol) {
//             result.push(currentGroup);
//             currentGroup = [];
//         }
//         currentGroup.push(val);
//     }
//     result.push(currentGroup);
//     return result;
// }
// export interface IFuncCalculate {
//     // getPositionValue: (position: IPosition) => IValue;
//     [key: string]: (args: TAllTypes[]) => TAllTypes[];
// }


// export abstract class FuncCalculate implements IPositionValue{
//     public abstract getValue(position: IPosition): IValue;
//     protected funcsMap: Map<string,AFunc> = new Map();
//     constructor() {
//         for(let item of DefaultFuncs) {
//             this.registerFunc(item.name, item.value);
//         }
//     }
//     public registerFunc(name: string, value: {new(args: IPositionValue): AFunc}) {
//         let funcInstance = new value(this);
//         this.funcsMap.set(name, funcInstance);
//     }
//     public removeFunc(name: string) {
//         this.funcsMap.delete(name);
//     }
//     public exist(name: string) {
//         return this.funcsMap.has(name);
//     }
//     public caculate(func: IFunction, args: TAllTypes[]) {

//     }
    // protected funcs = {
    //     SUM(vals: TAllTypes[]): TAllTypes[] {
    //         let sum = 0;
    //         for(let val of vals) {
    //             if(val.type == 'value' && (val.dataType === 'number' || val.dataType === 'string')) {
    //                 let _val = Number(val.value);
    //                 if(isNaN(_val) === false) {
    //                     // sum = (calculate('+', [sum, _val]) as IValueNumber).value;
    //                 }
    //             }
    //         }
    //         return [
    //             {
    //                 type: 'value',
    //                 dataType: 'number',
    //                 value: sum
    //             }
    //         ];
    //     },
    //     SUMIF(vals: TAllTypes[]): TAllTypes[] {
    //         let groupVals = formatValsToGroup(vals);
    //         if(groupVals.length !== 3) {
    //             throw new FormulaError('#N/A', {vals: vals});
    //         }
    //         type valueFromCell = Required<IExcelValue>&(IValueBoolean|IValueNumber|IValueString);
    //         let rangeVals = <any>groupVals[0] as valueFromCell[];
    //         let criteria = <any>groupVals as (IValueBoolean|IValueNumber|IValueString)[];
    //         let sumVals: TAllTypes[] = [];
    //         let group1 = groupByCol(rangeVals);
    //         let group2 = groupByCol(criteria as valueFromCell[]);
    //         let group3 = groupByCol(groupVals[2] as valueFromCell[]);
    //         if(criteria[0].rangeId !== undefined) { // 范围需要一一对应
    //             if(!Array.isArray(group1) ||!Array.isArray(group2)||!Array.isArray(group3)) {
    //                 throw new FormulaError('#N/A', {vals: vals, msg: 'group1,group2,group3', groups: [group1, group2,group3]});
    //             }
    //             for(let i = 0; i < Math.min(group1.length, group2.length, group3.length); i++) {
    //                 let rows1 = group1[i];
    //                 let rows2 = group2[i];
    //                 for(let cell of rows1.vals) {
    //                     let find = rows2.vals.find(item => item.row === cell.row);
    //                     if(!find) continue;
    //                     if(isEqualVal(cell, find)) {
    //                         let _find = group3[i].vals.find(item => item.row === cell.row);
    //                         _find && sumVals.push(_find);
    //                     }
    //                 }
    //             }
    //         } else {
    //             if(criteria[0].col !== undefined || criteria[0].row !== undefined) { // 来源于单元格的值
    //                 for(let i = 0; i < Math.min(group1.length, group3.length); i++) {
    //                     for(let cell of group1[i].vals) {
    //                         if(isEqualVal(cell, criteria[0])) {
    //                             let _find = group3[i].vals.find(item => item.row === cell.row);
    //                             _find && sumVals.push(_find);
    //                         }
    //                     }
    //                 }
    //             } else { // 直接输入的值
    //                 for(let i = 0; i < Math.min(group1.length, group3.length); i++) {
    //                     for(let cell of group1[i].vals) {
    //                         // 未判断特殊字符>5等情况
    //                         if(isEqualVal(cell, criteria[0])) {
    //                             let _find = group3[i].vals.find(item => item.row === cell.row);
    //                             _find && sumVals.push(_find);
    //                         }
    //                     }
    //                 }
    //             }
    //         }
    //         return this.SUM(sumVals);
    //     },
    //     AVGRAGE(vals: TAllTypes[]): TAllTypes[] {
    //         let sum = 0;
    //         let count = 0;
    //         for(let val of vals) {
    //             if(val.type == 'value' && (val.dataType === 'number' || val.dataType === 'string')) {
    //                 let _val = Number(val.value);
    //                 if(isNaN(_val) === false) {
    //                     sum +=  _val;
    //                     count++;
    //                 }
    //             }
    //         }
    //         if(count === 0) {
    //             return [{
    //                 type: 'value',
    //                 dataType: 'number',
    //                 value: 0
    //             }]
    //         }
    //         return []
    //         // return [calculate('/', [sum, count])];
    //     },
    //     AND(vals: TAllTypes[]): TAllTypes[] {
    //         if(vals.length === 0) {
    //             throw new FormulaError('#N/A', {vals: vals});
    //         }
    //         for(let val of vals) {
    //             if(!Boolean((val as IValueBoolean).value)) {
    //                 return [
    //                     {
    //                         type: 'value',
    //                         dataType: 'boolean',
    //                         value: false
    //                     }
    //                 ]
    //             }
    //         }
    //         return [{
    //             type: 'value',
    //             dataType: 'boolean',
    //             value: true
    //         }];
    //     },
    //     OR(vals: TAllTypes[]): TAllTypes[] {
    //         if(vals.length === 0) {
    //             throw new FormulaError('#N/A', {vals: vals});
    //         }
    //         for(let val of vals) {
    //             if(Boolean((val as IValueBoolean).value)) {
    //                 return [
    //                     {
    //                         type: 'value',
    //                         dataType: 'boolean',
    //                         value: true
    //                     }
    //                 ]
    //             }
    //         }
    //         return [{
    //             type: 'value',
    //             dataType: 'boolean',
    //             value: false
    //         }];
    //     },
    //     IF(vals: TAllTypes[]): TAllTypes[] {
    //         if(vals.length !== 3) {
    //             throw new FormulaError('#N/A', {vals: vals});
    //         }
    //         return vals[0] ? [vals[1]] : [vals[2]]
    //     },
    //     MAX(vals: TAllTypes[]): TAllTypes[] {
    //         if(vals.length === 0) {
    //             throw new FormulaError('#N/A', {vals: vals});
    //         }
    //         let result: IValueNumber = {
    //             type: 'value',
    //             dataType: 'number',
    //             value: 0
    //         }
    //         let res: IValueNumber|IValueString = vals.find(item => {
    //             return item.type === 'value' && (item.dataType === 'number' || item.dataType === 'string')
    //         }) as IValueNumber|IValueString;
    //         if(res && res.dataType === 'number') {
    //             result = res;
    //         }
    //         if(res.dataType === 'string') {
    //             result = parseToNumber(res)
    //         }
    //         for(let val of vals) {
    //             if(val.type === 'value' && (val.dataType === 'string' || val.dataType === 'number')) {
    //                 let _val = parseToNumber(val);
    //                 if(_val.value > result.value) {
    //                     result = _val;
    //                 }
    //             }
    //         }
    //         return [result];
    //     },
    //     MIN(vals: TAllTypes[]): TAllTypes[] {
    //         if(vals.length === 0) {
    //             throw new FormulaError('#N/A', {vals: vals});
    //         }
    //         let result: IValueNumber = {
    //             type: 'value',
    //             dataType: 'number',
    //             value: 0
    //         }
    //         let res: IValueNumber|IValueString = vals.find(item => {
    //             return item.type === 'value' && (item.dataType === 'number' || item.dataType === 'string')
    //         }) as IValueNumber|IValueString;
    //         if(res.dataType === 'string') {
    //             result = parseToNumber(res)
    //         }
    //         for(let val of vals) {
    //             if(val.type === 'value' && (val.dataType === 'string' || val.dataType === 'number')) {
    //                 let _val = parseToNumber(val);
    //                 if(_val.value < result.value) {
    //                     result = _val;
    //                 }
    //             }
    //         }
    //         return [result];
    //     },
    //     COUNTIF(vals: TAllTypes[]): TAllTypes[] {
    //         let groupVals = formatValsToGroup(vals);
    //         if(groupVals.length !== 3) {
    //             throw new FormulaError('#N/A', {vals: vals});
    //         }
    //         type valueFromCell = Required<IExcelValue>&(IValueBoolean|IValueNumber|IValueString);
    //         let rangeVals = <any>groupVals[0] as valueFromCell[];
    //         let criteria = <any>groupVals as (IValueBoolean|IValueNumber|IValueString)[];
    //         let group1 = groupByCol(rangeVals);
    //         let group2 = groupByCol(criteria as valueFromCell[]);
    //         let count = 0;
    //         if(criteria[0].rangeId !== undefined) { // 范围需要一一对应
    //             if(!Array.isArray(group1) ||!Array.isArray(group2)) {
    //                 throw new FormulaError('#N/A', {vals: vals, msg: 'group1,group2', groups: [group1, group2]});
    //             }
    //             for(let i = 0; i < Math.min(group1.length, group2.length); i++) {
    //                 let rows1 = group1[i];
    //                 let rows2 = group2[i];
    //                 for(let cell of rows1.vals) {
    //                     let find = rows2.vals.find(item => item.row === cell.row);
    //                     if(!find) continue;
    //                     if(isEqualVal(cell, find)) {
    //                         count++;
    //                     }
    //                 }
    //             }
    //         } else {
    //             if(criteria[0].col !== undefined || criteria[0].row !== undefined) { // 来源于单元格的值
    //                 for(let i = 0; i < group1.length; i++) {
    //                     for(let cell of group1[i].vals) {
    //                         if(isEqualVal(cell, criteria[0])) {
    //                             count++;
    //                         }
    //                     }
    //                 }
    //             } else { // 直接输入的值
    //                 for(let i = 0; i < group1.length; i++) {
    //                     for(let cell of group1[i].vals) {
    //                         if(isEqualVal(cell, criteria[0])) {
    //                             count++;
    //                         }
    //                     }
    //                 }
    //             }
    //         }
    //         return [{
    //             type: 'value',
    //             dataType: 'number',
    //             value: count
    //         }];
    //     }
    //     ,STDEV(vals: TAllTypes[]): TAllTypes[] {
    //         return [];
    //     },
    //     STDEVA(vals: TAllTypes[]): TAllTypes[] {
    //         return [];
    //     },
    //     VLOOKUP(vals: TAllTypes[]): TAllTypes[] {
    //         return [];
    //     },
    //     ROW(vals: TAllTypes[]): TAllTypes[] {
    //         let result = (<IExcelValue[]>vals).map<IValueNumber>((item) => {
    //             if(isNaN(Number(item.row))) {
    //                 throw new FormulaError("#N/A", {info: item, funcName: 'row'});
    //             }
    //             return {
    //                 type: 'value',
    //                 dataType: 'number',
    //                 value: item.row
    //             }
    //         })
    //         return result;
    //     },
    //     COLUMN(vals: TAllTypes[]): TAllTypes[] {
    //         let result = (<IExcelValue[]>vals).map<IValueNumber>((item) => {
    //             if(isNaN(Number(item.col))) {
    //                 throw new FormulaError("#N/A", {info: item, funcName: 'column'});
    //             }
    //             return {
    //                 type: 'value',
    //                 dataType: 'number',
    //                 value: item.col
    //             }
    //         })
    //         return result;
    //     },
    //     FIND(vals: TAllTypes[]): TAllTypes[] {
    //         if(vals.length > 0) {
    //             throw new FormulaError('#DIV/0!', {info: vals, msg: 'find error params'});
    //         }
    //         let target = vals[0] as IValueString;
    //         let source = vals[1] as IValueString;
    //         let options = (vals[2] ? vals[2] : {type: 'value', dataType: 'number', value: 1}) as IValueNumber;
    //         if(target.dataType !== 'string' || source.dataType !== 'string' || options.dataType !== 'number') {
    //             throw new FormulaError('#DIV/0!', {info: vals, msg: 'find error params type'});
    //         }
    //         let idx = source.value.indexOf(target.value);
    //         if(idx === -1 || idx + 1 < options.value) {
    //             throw new FormulaError('#VALUE!', {info: 'not find string index'});
    //         }
    //         return [{
    //             type: 'value',
    //             dataType: 'number',
    //             value: idx+1
    //         }];
    //     },
    //     LOOKUP(vals: TAllTypes[]): TAllTypes[] {
    //         return [];
    //     },
    //     MATCH(vals: TAllTypes[]): TAllTypes[] {
    
    //         return [];
    //     },
    //     INDEX(vals: TAllTypes[]): TAllTypes[] {
    
    //         return [];
    //     },
    //     SUMPRODUCT(vals: TAllTypes[]): TAllTypes[] {
    //         return [];
    //     },
    //     RAND(vals: TAllTypes[]): TAllTypes[] {
    //         if(vals.length !== 0) {
    //             throw new FormulaError('#VALUE!', {msg: 'rand not need params!'})
    //         }
    //         return [{
    //             type: 'value',
    //             dataType: 'number',
    //             value: Math.random()
    //         }];
    //     },
    //     RANDBETWEEN(vals: TAllTypes[]): TAllTypes[] {
    //         if(vals.length !== 2 || typeofValue(vals[0]) !== 'number' || typeofValue(vals[1]) !== 'number') {
    //             throw new FormulaError('#VALUE!', {msg:'randbetween params is not enough!params type is wrong!',vals: vals})
    //         }
    //         let val1 = Math.ceil((vals[0] as IValueNumber).value);
    //         let val2 = Math.ceil((vals[1] as IValueNumber).value);
    //         if(val1 >= val2) {
    //             throw new FormulaError('#VALUE!', {msg:'param error', vals: vals})
    //         }
    //         return [{
    //             type: 'value',
    //             dataType: 'number',
    //             value: Math.random()*(val2-val1+1)+val1
    //         }];
    //     },
    //     COUNTA(vals: TAllTypes[]): TAllTypes[] {
    //         let count = 0;
    //         for(let item of vals) {
    //             if(item.type === 'value' && item.dataType === 'string') {
    //                 if(item.value !== '') count++;
    //             } else {
    //                 count++;
    //             }
    //         }
    //         return [{
    //             type: 'value',
    //             dataType: 'number',
    //             value: count
    //         }];
    //     },
    //     MID(vals: TAllTypes[]): TAllTypes[] {
    //         if(vals.length !== 3 || typeofValue(vals[0]) !== 'string' || typeofValue(vals[1]) !== 'number' || typeofValue(vals[2]) !== 'number') {
    //             throw new FormulaError('#VALUE!', {msg: 'mid params is not enough ,or param type is wrong!', vals: vals});
    //         }
    //         let str = (vals[0] as IValueString).value;
    //         let startIdx = (vals[1] as IValueNumber).value;
    //         let len = (vals[2] as IValueNumber).value;
    //         if(startIdx < 0 || len < 0) {
    //             throw new FormulaError('#VALUE!', {msg: 'mid params is error!',vals: vals});
    //         }
    //         return [{
    //             type: 'value',
    //             dataType: 'string',
    //             value: str.substring(startIdx - 1, (startIdx + len - 1) > str.length ? str.length : (startIdx + len - 1))
    //         }];
    //     },
    //     MOD(vals: TAllTypes[]): TAllTypes[] {
    //         if(vals.length !== 2 || typeofValue(vals[0]) !== 'number' || typeofValue(vals[1]) !== 'number') {
    //             throw new FormulaError('#VALUE!', {msg: 'mod params is not enough ,or param type is wrong!'});
    //         }
    //         let val1 = vals[0] as IValueNumber;
    //         let val2 = vals[1] as IValueNumber;
    //         return [{
    //             type: 'value',
    //             dataType: 'number',
    //             value: val1.value % val2.value 
    //         }];
    //     },
    //     DATEDIF(vals: TAllTypes[]): TAllTypes[] {
    //         if(vals.length !== 3) {
    //             throw new FormulaError('#VALUE!', {msg: 'datedif params is erorr!', vals: vals});
    //         }
    //         let optionParam = ['y','m','d','md','ym','yd'];
    //         let startValue = vals[0] as IValueNumber;
    //         let endValue = vals[1] as IValueNumber;
    //         let unitValue = vals[2] as IValueString;
    //         if((typeofValue(unitValue) !== 'string' || optionParam.indexOf(unitValue.value) === -1)
    //             || typeofValue(startValue) !== 'number' || startValue.value < 0
    //             || typeofValue(endValue) !== 'number' || endValue.value < 0
    //         ) {
    //             throw new FormulaError('#VALUE!', {msg: 'datedif unit is erorr!', vals: vals});
    //         }
    //         let unit = ((vals[2] as IValueString).value) as 'y'|'m'|'d'|'md'|'ym'|'yd';
    //         let startDate = new Date(startValue.value * DAY_MUNIS);
    //         let endDate = new Date(endValue.value * DAY_MUNIS);
    //         if(unit === 'y') {
    //             return [{
    //                 type: 'value',
    //                 dataType: 'number',
    //                 value: endDate.getFullYear() - startDate.getFullYear()
    //             }];
    //         }
    //         if(unit === 'm') {
    //             return [{
    //                 type: 'value',
    //                 dataType: 'number',
    //                 value: (endDate.getFullYear() - startDate.getFullYear()) * 12 + (endDate.getMonth() - startDate.getMonth())
    //             }]
    //         }
    //         if(unit === 'd') {
    //             return [{
    //                 type: 'value',
    //                 dataType: 'number',
    //                 value: endValue.value - startValue.value
    //             }]
    //         }
    //         if(unit === 'md') {
    //             throw new FormulaError('#VALUE!', {msg: 'datedif not support yd、md、ym!'})
    //         }
    //         return [];
    //     },
    //     TODAY(vals: TAllTypes[]): TAllTypes[] {
    //         let date = new Date();
    //         let dateToNum = date.getTime() / DAY_MUNIS;
    //         return [
    //             {
    //                 type: 'value',
    //                 dataType: 'number',
    //                 value: dateToNum
    //             }
    //         ];
    //     },
    //     TEXT(vals: TAllTypes[]): TAllTypes[] {
    //         let result = (vals as (IValueNumber|IValueString|any)[]).map(item => {
    //             if(item.dataType !== 'string' || item.dataType !== 'number') {
    //                 throw new FormulaError('#VALUE!', {info: 'function text value is not string or number', vals: vals})
    //             }
    //             return {
    //                 type: 'value',
    //                 dataType: 'string',
    //                 value: String(item.value)
    //             }
    //         }) as TAllTypes[];
    //         return result;
    //     }
    // }

// }